package main

import (
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
)

const (
	TKSTR = "suikhan@"
)

func main() {
	var host string
	var port int64
	var down string
	var exef string
	var force bool
	flag.StringVar(&host, "host", "127.0.0.1", "升级服务IP地址")
	flag.Int64Var(&port, "port", 1978, "升级服务端口号")
	flag.StringVar(&down, "file", "%", "要升级的文件名称(%-全部)")
	flag.StringVar(&exef, "exec", "", "升级完毕后执行程序名称")
	flag.BoolVar(&force, "force", false, "强制升级程序")
	flag.Parse()
	url := fmt.Sprintf("http://%s:%d", host, port)
	files, err := GetFiles(url, down)
	if err != nil {
		log.Println("GetFiles错误：", err)
		return
	}
	cnt := len(files)
	for idx, fnc := range files {
		if NotSelf(fnc.NAME) && (force || NeedUp(fnc)) {
			fc, err := GetContent(url, fnc.NAME)
			if err != nil {
				log.Printf("[%d/%d]下载文件[%s]错误：\n%s\n", idx+1, cnt, fnc.NAME, err.Error())
				continue
			}
			MakeDir(fc.NAME)
			err = os.WriteFile(fc.NAME, fc.CTX, os.ModePerm)
			if err != nil {
				log.Printf("[%d/%d]写入文件[%s]错误：\n%s\n", idx+1, cnt, fnc.NAME, err.Error())
				continue
			}
			log.Printf("[%d/%d]升级文件[%s]完成！\n", idx+1, cnt, fnc.NAME)
		}
	}
	if exef != "" {
		cd := exec.Command(exef)
		cd.Start()
	}
}

func NotSelf(fn string) bool {
	_, dir := filepath.Split(fn)
	return dir != "upc.exe"
}

func MakeDir(fn string) {
	if !strings.Contains(fn, "\\") {
		return
	}
	dir, _ := filepath.Split(fn)
	if FileExists(dir) {
		return
	}
	os.MkdirAll(dir, os.ModePerm)
}

func NeedUp(fnc FileNoCtx) bool {
	fn := fnc.NAME
	if !FileExists(fn) {
		return true
	}
	bytes, err := os.ReadFile(fn)
	if err != nil {
		return true
	}
	ver := GetExeVer(fn)
	if ver != fnc.VER {
		return true
	}
	md := md5.New()
	md.Write(bytes)
	str := hex.EncodeToString(md.Sum(nil))
	if str != fnc.MD5 {
		return true
	}

	f, err := os.Open(fn)
	if err != nil {
		return true
	}
	defer f.Close()

	fi, err := f.Stat()
	if err != nil {
		return true
	}
	if fi.ModTime().Format("2006-01-02 15:04:05") != fnc.CTM {
		return true
	}
	return false
}

func GetFiles(url string, val string) ([]FileNoCtx, error) {
	newurl := fmt.Sprintf("%s/files", url)
	str := TKSTR + val
	md := md5.New()
	md.Write([]byte(str))
	str = hex.EncodeToString(md.Sum(nil))
	reqdata := ReqData{
		Value: val,
		Token: str,
	}
	bytes, err := json.Marshal(reqdata)
	if err != nil {
		log.Println("构造请求JSON数据发生错误：", err)
		return nil, err
	}
	req, err := http.NewRequest(http.MethodPost, newurl, strings.NewReader(string(bytes)))
	if err != nil {
		log.Println("构造POST请求发生错误：", err)
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var respdata RespDataFnc
	err = json.NewDecoder(resp.Body).Decode(&respdata)
	if err != nil {
		return nil, err
	}
	return respdata.Data, nil
}

func GetContent(url string, val string) (*FileCtx, error) {
	newurl := fmt.Sprintf("%s/content", url)
	str := TKSTR + val
	md := md5.New()
	md.Write([]byte(str))
	str = hex.EncodeToString(md.Sum(nil))
	reqdata := ReqData{
		Value: val,
		Token: str,
	}
	bytes, err := json.Marshal(reqdata)
	if err != nil {
		log.Println("构造请求JSON数据发生错误：", err)
		return nil, err
	}
	req, err := http.NewRequest(http.MethodPost, newurl, strings.NewReader(string(bytes)))
	if err != nil {
		log.Println("构造POST请求发生错误：", err)
		return nil, err
	}
	req.Header.Set("Content-Type", "application/json;charset=UTF-8")
	client := &http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	var respdata RespDataFc
	err = json.NewDecoder(resp.Body).Decode(&respdata)
	if err != nil {
		return nil, err
	}
	return &respdata.Data, nil
}
